package com.demo.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Scanner;

@Slf4j
public class EventLoopClient {

    public static void main(String[] args) throws InterruptedException {
        connectSync();
        connectAddLinstener();
        closeSync();
    }

    /**
     * 通过 sync() 阻塞线程， 等待connect结果
     *
     * @throws InterruptedException
     */
    private static void connectSync() throws InterruptedException {
        ChannelFuture cf = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {

                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                //connect方法为异步非阻塞，即main发起调用；nioeventloop中的某个selector执行了connect
                // 连接本身耗时较长，可能需要1s
                .connect(new InetSocketAddress("localhost", 8080));
        //阻塞线程，直到connect成功， 也就是通过sync 同步结果， main thread继续运行下面的操作
        cf.sync();
        Channel channel = cf.channel();
        channel.writeAndFlush("Hello World");
    }

    /**
     * 通过 设置回调对象 ， 处理异步结果
     */
    private static void connectAddLinstener() {
        ChannelFuture cf = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8080));

        //nioeventloop thread 执行下述操作
        cf.addListener(new ChannelFutureListener() {
            //nio connect成功之后，会调用 operationComplete()
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                future.channel().writeAndFlush("Hello World");
            }
        });
    }

    private static void closeSync() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        ChannelFuture channelFuture = new Bootstrap()
                .group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8080));
        Channel channel = channelFuture.sync().channel();
        log.info("abc,{}", channel);
        new Thread(() -> {
            while (true) {
                Scanner scanner = new Scanner(System.in);
                String line = scanner.nextLine();
                if ("q".equals(line)) {
                    // channel 被绑定在某一个nioeventloop中
                    // 这条代码在新开的thread 中调用， 但实际关闭channel的线程 是绑定的某一个nioeventloop
                    channel.close();
                    break;
                }
                channel.writeAndFlush(line);
            }
        }, "input").start();

        ChannelFuture closeFuture = channel.closeFuture();
        closeFuture.sync();
        group.shutdownGracefully();
        log.info("closeFuture2,{}", closeFuture);
    }

    private static void closeAddListener() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        ChannelFuture channelFuture = new Bootstrap()
                .group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8080));
        Channel channel = channelFuture.sync().channel();
        log.info("abc,{}", channel);
        new Thread(() -> {
            while (true) {
                Scanner scanner = new Scanner(System.in);
                String line = scanner.nextLine();
                if ("q".equals(line)) {
                    // channel 被绑定在某一个nioeventloop中
                    // 这条代码在新开的thread 中调用， 但实际关闭channel的线程 是绑定的某一个nioeventloop
                    channel.close();
                    break;
                }
                channel.writeAndFlush(line);
            }
        }, "input").start();

        ChannelFuture closeFuture = channel.closeFuture();
        closeFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                log.info("closeFuture2,{}", closeFuture);
                //优雅得 停机
                group.shutdownGracefully();
            }
        });
    }

}
